package com.hundsun.cprs.yyt.service.goods.service.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.BoundZSetOperations;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
import org.springframework.stereotype.Service;

import com.hundsun.cprs.yyt.common.base.BaseService;
import com.hundsun.cprs.yyt.common.enums.EnumAreaCode;
import com.hundsun.cprs.yyt.common.redis.RedisDistributedLock;
import com.hundsun.cprs.yyt.service.goods.domain.query.GoodsQuery;
import com.hundsun.cprs.yyt.service.goods.dto.GoodsInfo;
import com.hundsun.cprs.yyt.service.goods.service.GoodsCacheQueryService;
import com.hundsun.cprs.yyt.service.goods.service.GoodsService;

@Service
public class GoodsCacheQueryServiceImpl extends BaseService implements GoodsCacheQueryService {
    
    @Autowired
    @Qualifier("stringRedisTemplate")
    private RedisTemplate<String,String> stringRedisTemplate;
    @Autowired
    private RedisTemplate<String,Object> redisTemplate;
    @Autowired
    private GoodsService goodsService;
    @Autowired
    private RedisDistributedLock redisLock;
    
    @Value("${goods.cache.timeToLiveSeconds:120}")
    private Integer timeToLiveSeconds;

    @Override
    public void selectByPage(GoodsQuery query) {
        String cacheKey = obtainCacheKey(query);
        if (cacheKey == null) {
            return;
        }
        
        checkAndInit(cacheKey,query);
        
        BoundZSetOperations<String,String> zSetOper = stringRedisTemplate.boundZSetOps(cacheKey);
        Long totalCount = zSetOper.zCard();
        if (totalCount > 0) {
            Integer beginIndex = ((query.getPageNo() - 1) * query.getPageSize());
            Integer endIndex = (beginIndex + query.getPageSize() - 1);
            List<String> goodsIds = new ArrayList<String>(zSetOper.reverseRange(beginIndex, endIndex));
            
            query.setData(loadGoodsInfoFromCache(cacheKey,goodsIds));
            query.setTotalCount(totalCount.intValue());
        }
    }
    
    protected String obtainCacheKey(GoodsQuery query) {
        if (StringUtils.isEmpty(query.getAreaCode())) {
            return null;
        }
        StringBuilder sb = new StringBuilder("YYT:AREA:");
        if (query.getAreaCode().equals(EnumAreaCode.WHOLESALE_AREA.getCode())) {
            sb.append(EnumAreaCode.WHOLESALE_AREA.getCode().toUpperCase());
        } else {
            sb.append(EnumAreaCode.RETAIL_AREA.getCode().toUpperCase());
        }
        List<String> conditions = new ArrayList<String>();
        if (StringUtils.isNotEmpty(query.getIsExplosion()) && query.getIsExplosion().equals("Y")) {
            conditions.add("EXPLOSION");
        }
        if (StringUtils.isNotEmpty(query.getStatus()) && query.getStatus().equals("1")) {
            conditions.add("PUBLISH");
        }
        if (query.getPlateId() != null) {
            conditions.add("PLATE_" + query.getPlateId());
        }
        if (conditions.isEmpty()) {
            conditions.add("ALL");
        }
        sb.append(":").append(StringUtils.join(conditions.iterator(), "&"));
        return sb.toString();
    }
    
    /**
     * 检查key是否存在,不存在则加载DB数据
     * @param cacheKey
     * @param query
     */
    protected void checkAndInit(String cacheKey,GoodsQuery query){
        boolean exists = stringRedisTemplate.hasKey(cacheKey);
        if (!exists) {
            //初始化DB的数据
            String lockKey = new StringBuffer(cacheKey).append("_LOCK").toString();
            try {
                if(redisLock.lock(lockKey)){
                    exists = stringRedisTemplate.hasKey(cacheKey);
                    if (!exists) {
                        GoodsQuery dbQuery = new GoodsQuery();
                        BeanUtils.copyProperties(query, dbQuery);
                        dbQuery.onlyReturnList();
                        dbQuery.setPageSize(100000);//全部拉取
                        
                        if (dbQuery.getPlateId() != null) {
                            goodsService.queryGoodsPlateList(dbQuery);
                        } else {
                            goodsService.queryAreaGoodsList(dbQuery);
                        }
                        Set<TypedTuple<String>> goodsIdsSet = new HashSet<TypedTuple<String>>();
                        for(GoodsInfo goods : dbQuery.getData()){
                            String value = goods.getId().toString();
                            //排序规则优先爆款优先发布时间
                            double score = new BigDecimal(new StringBuilder(goods.getTopShow()).append(goods.getPublishTime().getTime()).toString()).doubleValue();
                            goodsIdsSet.add(new DefaultTypedTuple<String>(value,score));
                        }
                        
                        if (!goodsIdsSet.isEmpty()) {
                            stringRedisTemplate.boundZSetOps(cacheKey).add(goodsIdsSet);
                            stringRedisTemplate.boundZSetOps(cacheKey).expire(timeToLiveSeconds, TimeUnit.SECONDS);
                        }
                        
                    }
                }
            } finally {
                redisLock.releaseLock(lockKey);
            }
        }
    }
    
    protected List<GoodsInfo> loadGoodsInfoFromCache(String cacheKey,List<String> keys){
        List<GoodsInfo> pageResult = null;
        
        //解析专区代码-格式为YYT:AREA:[AREA_CODE]:[CONDITION]
        String areaCode = cacheKey.split(":")[2];
        
        List<String> goodsKeys = new ArrayList<String>();
        for(String key : keys){
            goodsKeys.add(new StringBuilder("YYT:AREA:GOODS:").append(areaCode).append(":").append(key).toString());
        }
        
        List<Object> result = redisTemplate.opsForValue().multiGet(goodsKeys);
        if (CollectionUtils.isNotEmpty(result)) {
            pageResult = new ArrayList<GoodsInfo>(result.size());
            for(int i = 0; i < result.size() ;i++){
                // 如果缓存返回空,则加载一次
                GoodsInfo goods = null;
                if (result.get(i) == null) {
                    goods = goodsService.selectGoodsInfoByIdFromCache(Integer.parseInt(keys.get(i)), areaCode.toLowerCase());
                } else {
                    goods = (GoodsInfo)result.get(i);
                }
                
                pageResult.add(goods);
            }
        }
        return pageResult;
    }
    

}
